package com.mm.plugin.logging;


import cn.hutool.json.JSONUtil;
import com.mm.plugin.logging.annotation.AopLogger;
import com.mm.plugin.logging.async.LoggingFactory;
import com.mm.plugin.logging.enums.BusinessType;
import com.mm.plugin.logging.enums.LoggingType;
import com.mm.entity.MmBaseLog;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class AopLoggerAspect {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    LoggingFactory loggingFactory;

    @Pointcut("@annotation(com.mm.plugin.logging.annotation.AopLogger)")
    public void aopLoggerAspect() {
    }

    /**
     * 环绕触发
     *
     * @param point
     * @return
     */
    @Around("aopLoggerAspect()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        Object result = null;
        MmBaseLog log = new MmBaseLog();
        long startTime = System.currentTimeMillis();
        try {
            AopLogger logger = getAopLoggerDescribe(point);
            String describe = logger.describe();
            BusinessType type = logger.type();
            log.setTitle(StringUtils.isBlank(describe)?"-":describe);
            log.setBusinessType(type);
            log.setSuccess(true);
            log.setLoggingType(LoggingType.OPERATE);
            result = point.proceed();
            Map<String, Object> fieldsName = getFieldsName(point);
            log.setRequestBody(JSONUtil.toJsonStr(fieldsName));
            log.setResponseBody(JSONUtil.toJsonStr(result));

        } catch (Throwable throwable) {
//            throwable.printStackTrace();
            log.setSuccess(false);
            log.setErrorMsg(throwable.getMessage());
//            logger.error(throwable.getMessage());
            throw throwable;
        }finally {
            loggingFactory.record(log);
        }

        // 打印请求相关参数
        logger.info("========================================== Start ==========================================");
//        logger.info("Describe       : {}", describe);
        // 打印请求 url
        logger.info("URL            : {}", request.getRequestURL());
        logger.info("URI            : {}", request.getRequestURI());
        // 打印 Http method
        logger.info("HTTP Method    : {}", request.getMethod());
        // 打印调用 controller 的全路径以及执行方法
        logger.info("Class Method   : {}.{}", point.getSignature().getDeclaringTypeName(), point.getSignature().getName());
        // 打印请求的 IP
        logger.info("IP             : {}", request.getHeader("x-forwarded-for") == null?request.getRemoteAddr():request.getHeader("x-forwarded-for"));
        // 打印请求入参
        logger.info("Request Args   : {}", point.getArgs());
        // 打印请求出参
        logger.info("Response Args  : {}", result);
        logger.info("Time Consuming : {} ms", System.currentTimeMillis() - startTime);
        logger.info("=========================================== End ===========================================");
        return result;
    }

    /**
     * 获取注解中对方法的描述信息
     *
     * @param joinPoint 切点
     * @return describe
     */
    public static AopLogger getAopLoggerDescribe(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        AopLogger controllerLog = method.getAnnotation(AopLogger.class);
        return controllerLog;
    }



    /**
     * 获取参数列表
     *
     * @param joinPoint
     * @return
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     */
    private static Map<String, Object> getFieldsName(ProceedingJoinPoint joinPoint) {
        // 参数值
        Object[] args = joinPoint.getArgs();
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = pnd.getParameterNames(method);
        Map<String, Object> paramMap = new HashMap<>(32);
        for (int i = 0; i < parameterNames.length; i++) {
            paramMap.put(parameterNames[i], args[i]);
        }
        return paramMap;


    }
}
